梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)
动态规划的本质:它是一种 “以空间换时间”的优化算法,核心是把复杂问题拆解为多个重叠的子问题,通过记录子问题的最优解(状态),避免重复计算,最终由子问题的最优解推导出原问题的最优解。
最值型问题是动态规划的入门级也是最核心的应用,核心需求是求问题的最优解(最大 / 最小 / 最长 / 最短),比如最长子序列、最大和、最小花费、最短路径等。这类问题的暴力解法通常是递归枚举所有可能,存在大量重叠子问题,DP 通过记录子问题的最值,直接推导原问题最值。
问题描述:
算法解析:
dp 数组计算过程:
输入数组:[-2,1,-3,4,-1,2,1,-5,4]
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int maxSubArray(vector<int>& nums) {
// 处理边界情况
if (nums.empty()) return 0;
int n = nums.size();
// 定义 dp 数组,dp[i] 表示以 nums[i] 结尾的最大子数组和
vector<int> dp(n);
// 初始化 dp 数组第一个元素
dp[0] = nums[0];
// 填充 dp 数组
for (int i = 1; i < n; ++i) {
// 状态转移:要么重新开始,要么加入前一个子数组
dp[i] = max(nums[i], dp[i-1] + nums[i]);
}
// 遍历 dp 数组,找到最大值(即全局最大子数组和)
int max_sum = dp[0];
for (int val : dp) {
max_sum = max(max_sum, val);
}
return max_sum;
}
int main() {
// 测试用例1:输出 6
vector<int> nums1 = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
cout << "Test Case 1: " << maxSubArray(nums1) << endl;
// 测试用例2:输出 1
vector<int> nums2 = {1};
cout << "Test Case 2: " << maxSubArray(nums2) << endl;
// 测试用例3:输出 23
vector<int> nums3 = {5, 4, -1, 7, 8};
cout << "Test Case 3: " << maxSubArray(nums3) << endl;
return 0;
}
Test Case 1: 6
Test Case 2: 1
Test Case 3: 23
问题描述:
算法解析:
dp 数组计算过程:
输入数组:[10,9,2,5,3,7,101,18]
#include <iostream>
#include <vector>
using namespace std;
int lengthOfLIS(vector<int>& nums) {
if (nums.empty()) return 0;
int n = nums.size();
vector<int> dp(n, 1); // 初始化每个位置的最长子序列长度为1
int max_len = 1; // 记录全局最大值
for (int i = 1; i < n; ++i) {
// 遍历i之前的所有元素,寻找可以接在后面的递增序列
for (int j = 0; j < i; ++j) {
if (nums[j] < nums[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
// 更新全局最长长度
max_len = max(max_len, dp[i]);
}
return max_len;
}
int main() {
// 测试用例1:输出 4(最长子序列是 [10,9,2,5,3,7,101,18] 中的 [2,3,7,101])
vector<int> nums1 = {10,9,2,5,3,7,101,18};
cout << "Test Case 1: " << lengthOfLIS(nums1) << endl;
// 测试用例2:输出 4
vector<int> nums2 = {0,1,0,3,2,3};
cout << "Test Case 2: " << lengthOfLIS(nums2) << endl;
// 测试用例3:输出 1
vector<int> nums3 = {7,7,7,7,7,7,7};
cout << "Test Case 3: " << lengthOfLIS(nums3) << endl;
return 0;
}
Test Case 1: 4
Test Case 2: 4
Test Case 3: 1
问题描述:
算法解析:
dp 数组计算过程:
输入:text1 = "abcde"(m=5),text2 = "ace"(n=3)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int longestCommonSubsequence(string text1, string text2) {
int m = text1.size();
int n = text2.size();
// 定义二维DP数组,dp[i][j]表示text1前i个字符和text2前j个字符的LCS长度
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// 填充DP数组
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (text1[i-1] == text2[j-1]) {
// 字符匹配,继承左上角值+1
dp[i][j] = dp[i-1][j-1] + 1;
} else {
// 字符不匹配,取左边或上边的最大值
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
int main() {
// 测试用例1:输出 3
string text1_1 = "abcde";
string text2_1 = "ace";
cout << "Test Case 1: " << longestCommonSubsequence(text1_1, text2_1) << endl;
// 测试用例2:输出 0
string text1_2 = "abc";
string text2_2 = "def";
cout << "Test Case 2: " << longestCommonSubsequence(text1_2, text2_2) << endl;
// 测试用例3:输出 4
string text1_3 = "abcba";
string text2_3 = "abcbcba";
cout << "Test Case 3: " << longestCommonSubsequence(text1_3, text2_3) << endl;
return 0;
}
Test Case 1: 3
Test Case 2: 0
Test Case 3: 5
问题描述:
算法解析:
dp 数组计算过程:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int minPathSum(vector<vector<int>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int m = grid.size(); // 行数
int n = grid[0].size(); // 列数
// 定义DP数组,dp[i][j]表示从(0,0)到(i,j)的最小路径和
vector<vector<int>> dp(m, vector<int>(n, 0));
dp[0][0] = grid[0][0]; // 初始化起点
// 填充第一行(只能从左边来)
for (int j = 1; j < n; ++j) {
dp[0][j] = dp[0][j-1] + grid[0][j];
}
// 填充第一列(只能从上面来)
for (int i = 1; i < m; ++i) {
dp[i][0] = dp[i-1][0] + grid[i][0];
}
// 填充其余位置(取左边/上边最小值 + 当前值)
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}
}
return dp[m-1][n-1];
}
int main() {
// 测试用例1:输出 7
vector<vector<in>> grid1 = {{1,3,1},{1,5,1},{4,2,1}};
cout << "Test Case 1: " << minPathSum(grid1) << endl;
// 测试用例2:输出 12
vector<vector<int>> grid2 = {{1,2,3},{4,5,6}};
cout << "Test Case 2: " << minPathSum(grid2) << endl;
return 0;
}
Test Case 1: 7
Test Case 2: 12
问题描述:
算法解析:
dp 数组计算过程:
输入:n=5
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int climbStairs(int n) {
// 处理边界情况
if (n == 1) return 1;
if (n == 2) return 2;
// 定义DP数组,dp[i]表示到达第i阶的方法数
vector<int> dp(n + 1);
dp[1] = 1;
dp[2] = 2;
// 填充DP数组
for (int i = 3; i <= n; ++i) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
int main() {
cout << "n=2: " << climbStairs(2) << endl; // 输出 2
cout << "n=3: " << climbStairs(3) << endl; // 输出 3
cout << "n=5: " << climbStairs(5) << endl; // 输出 8
return 0;
}
n=2: 2
n=3: 3
n=5: 8